/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

namespace OptixDotNet 
{
	ref class Buffer;
	ref class TextureSampler;

	/// <summary>
	/// Controls the texture addressing wrap method of texture coordinates
	/// </summary>
	public enum class WrapMode
	{
		/// <summary>
		/// Texture coordinates are wrapped back around to the [0,1] range when they go outside the [0,1] range.
		/// </summary>
		Repeat	= RT_WRAP_REPEAT,

		/// <summary>
		/// Texture coordinates are clamped to the [0,1] range.
		/// </summary>
		Clamp	= RT_WRAP_CLAMP_TO_EDGE
	};

	/// <summary>
	/// Controls the filtering mode for texture fetches
	/// </summary>
	public enum class FilterMode
	{
		/// <summary>
		/// No texture filtering.
		/// </summary>
		None = RT_FILTER_NONE,

		/// <summary>
		/// Finds the closest texture sample to the texture coordinate.
		/// </summary>
		Nearest = RT_FILTER_NEAREST,

		/// <summary>
		/// Performs a bilinear or bicubic interpolation of the texture sample for 2D and 3D textures respectively.
		/// </summary>
		Linear = RT_FILTER_LINEAR
	};

	/// <summary>
	/// Controls the interpretation of texture samples. 
	/// </summary>
	public enum class TextureReadMode
	{
		/// <summary>
		/// Data returned by a sample will be in the original ElementType range
		/// </summary>
		ElementType		= RT_TEXTURE_READ_ELEMENT_TYPE,

		/// <summary>
		/// Data returned by a sample will be in a normalized [0,1] range
		/// </summary>
		NormalizedFloat	= RT_TEXTURE_READ_NORMALIZED_FLOAT
	};

	/// <summary>
	/// Controls the interpretation of texture coordinates. 
	/// </summary>
	public enum class TextureIndexMode
	{
		/// <summary>
		/// NormalizeCoords will parameterize the texture over [0,1].
		/// </summary>
		NormalizeCoords = RT_TEXTURE_INDEX_NORMALIZED_COORDINATES,

		/// <summary>
		/// ArrayIndex will interpret texture coordinates as array indices [0, N] into the underlying buffer
		/// </summary>
		ArrayIndex		= RT_TEXTURE_INDEX_ARRAY_INDEX
	};

	/// <summary>
	/// Holds <see cref="OptixDotNet::WrapMode">WrapModes</see> for U, V, and W texture dimensions.
	/// </summary>
	public value struct WrapUVW
	{
		WrapMode WrapU;
		WrapMode WrapV;
		WrapMode WrapW;

		static property WrapUVW Clamp
		{
			inline WrapUVW get()
			{
				WrapUVW wrap = { WrapMode::Clamp, WrapMode::Clamp, WrapMode::Clamp };
				return wrap;
			}
		}

		static property WrapUVW Repeat
		{
			inline WrapUVW get()
			{
				WrapUVW wrap = { WrapMode::Repeat, WrapMode::Repeat, WrapMode::Repeat };
				return wrap;
			}
		}
	};

	/// <summary>
	/// Holds <see cref="OptixDotNet::FilterMode">FilterModes</see> for Minification, Magnification, and Mipmaps.
	/// </summary>
	public value struct FilterMinMagMip
	{
		FilterMode Min;
		FilterMode Mag;
		FilterMode Mip;
	};

	/// <summary>
	/// Description data used to create a <see cref="OptixDotNet::TextureSampler">TextureSampler</see>.
	/// </summary>
	public value struct TextureSamplerDesc
	{
		/// <summary>
		/// <see cref="OptixDotNet::WrapUVW">Wrap modes</see> for the TextureSampler.
		/// </summary>
		WrapUVW				Wrap;

		/// <summary>
		/// <see cref="OptixDotNet::FilterMinMagMip">Filter modes</see> for the TextureSampler.
		/// </summary>
		FilterMinMagMip		Filter;

		/// <summary>
		/// <see cref="OptixDotNet::TextureReadMode">TextureReadMode</see> for the TextureSampler.<br/>
		/// For ElementType, data returned by a sample will be in the original ElementType range<br/>
		/// For NormalizedFloat, data returned by a sample will be in a normalized [0,1] range
        /// </summary>
		TextureReadMode		Read;

		/// <summary>
		/// <see cref="OptixDotNet::TextureIndexMode">Texture indexing mode</see> for the TextureSampler.
		/// Controls the interpretation of texture coordinates. 
		/// NormalizeCoords will parameterize the texture over [0,1].
		/// ArrayIndex will interpret texture coordinates as array indices into the underlying buffer
        /// </summary>
		TextureIndexMode	Index;

		/// <summary>
		/// Number of mip map levels for the sampler
        /// </summary>
		unsigned int		MipLevels;

		/// <summary>
		/// Max anisotropy value for anisotropic filtering. A value > 0.0f will enable anisotropic filtering.
        /// </summary>
		float				MaxAnisotropy;

		inline static TextureSamplerDesc GetDefault( WrapMode wrapmode )
		{
			WrapUVW wrap = { wrapmode, wrapmode, wrapmode };

			TextureSamplerDesc desc = Default;
			desc.Wrap = wrap;

			return desc;
		}

		/// <summary>
		/// Gets a default TextureSamplerDesc with settings:
		/// WrapUVW = [ Clamp, Clamp, Clamp ]
		/// Filter = [ Linear, Linear, Linear ]
		/// Read = NormalizedFloat
		/// Index = NormalizeCoords
		/// MipLevels = 1
		/// MaxAnisotropy = 0.0f
		/// </summary>
		static property TextureSamplerDesc Default
		{
			inline TextureSamplerDesc get()
			{
				WrapUVW wrap = { WrapMode::Clamp, WrapMode::Clamp, WrapMode::Clamp };
				FilterMinMagMip filter = { FilterMode::Linear, FilterMode::Linear, FilterMode::None };
				TextureSamplerDesc desc = { wrap, filter, TextureReadMode::NormalizedFloat, TextureIndexMode::NormalizeCoords, 1, 0.0f };
				return desc;
			}
		}
	};

	/// <summary>
	/// Holds a one or more buffers buffer used common texture mapping functionality, such as: filtering and sampling.
	/// </summary>
	public ref class TextureSampler : public DataNode
	{
	public:

		/// <summary>
		/// Create a TextureSampler from a <see cref="OptixDotNet::TextureSamplerDesc">TextureSamplerDesc</see>.
		/// </summary>
		/// <param name="context">Optix context</param>
		/// <param name="desc">Description of the TextureSampler</param>
		TextureSampler( Context^ context, TextureSamplerDesc desc );

		/// <summary>
		/// Sets the TextureSampler settings defined in the <see cref="OptixDotNet::TextureSamplerDesc">TextureSamplerDescription</see>.
		/// </summary>
		void SetDesc( TextureSamplerDesc% desc );

		/// <summary>
		/// Checks to make sure the object is valid and consistent
        /// </summary>
		virtual void Validate() override
		{
			CheckError( rtTextureSamplerValidate( mSampler ) );
		}

		/// <summary>
		/// Destroys the Optix resource
        /// </summary>
		virtual void Destroy() override
		{
			if( mSampler )
				CheckError( rtTextureSamplerDestroy( mSampler ) );

			mSampler = 0;
		}

		/// <summary>
		/// Attaches a Buffer object to the Texture Sampler. Used to fill arbitrary texture data, in place of creating from a DX/GL texture.
        /// </summary>
		virtual void SetBuffer( Buffer^ buffer, unsigned int mipLevel );

		/// <summary>
		/// Gets the Texture addressing mode for U, V, W coordinates
        /// </summary>
		property WrapUVW Wrap
		{
			WrapUVW get()
			{
				RTwrapmode u, v, w;

				CheckError( rtTextureSamplerGetWrapMode( mSampler, 0, &u ) );
				CheckError( rtTextureSamplerGetWrapMode( mSampler, 1, &v ) );
				CheckError( rtTextureSamplerGetWrapMode( mSampler, 2, &w ) );

				WrapUVW mode = { static_cast<WrapMode>( u ), static_cast<WrapMode>( v ), static_cast<WrapMode>( w ) };
				return mode;
			}
		}

		/// <summary>
		/// Gets the Texture filtering mode for the sampler
        /// </summary>
		property FilterMinMagMip Filter
		{
			FilterMinMagMip get()
			{
				RTfiltermode min, mag, mip;
				CheckError( rtTextureSamplerGetFilteringModes( mSampler, &min, &mag, &mip ) );

				FilterMinMagMip mode = { static_cast<FilterMode>( min ), static_cast<FilterMode>( mag ), static_cast<FilterMode>( mip ) };
				return mode;
			}
		}

		/// <summary>
		/// Gets the texture read mode for the sampler.
        /// </summary>
		property TextureReadMode ReadMode
		{
			TextureReadMode get()
			{
				RTtexturereadmode mode;
				CheckError( rtTextureSamplerGetReadMode( mSampler, &mode ) );

				return static_cast<TextureReadMode>( mode );
			}
		}

		/// <summary>
		/// Gets the texture indexing mode for the sampler.
        /// </summary>
		property TextureIndexMode IndexingMode
		{
			TextureIndexMode get()
			{
				RTtextureindexmode mode;
				CheckError( rtTextureSamplerGetIndexingMode( mSampler, &mode ) );

				return static_cast<TextureIndexMode>( mode );
			}
		}

		/// <summary>
		/// Gets the number of mip map levels for the sampler
        /// </summary>
		property unsigned int MipLevelCount
		{
			unsigned int get()
			{
				unsigned int count;
				CheckError( rtTextureSamplerGetMipLevelCount( mSampler, &count ) );

				return count;
			}
		}

		/// <summary>
		/// Gets the max anisotropy
        /// </summary>
		property float MaxAnisotropy
		{
			float get()
			{
				float ani;
				CheckError( rtTextureSamplerGetMaxAnisotropy( mSampler, &ani ) );

				return ani;
			}
		}

	internal:
		inline RTtexturesampler InternalPtr() { return mSampler; }
		virtual RTobject ObjectPtr() override { return InternalPtr(); }

	protected:
		TextureSampler( Context^ context );

		RTtexturesampler mSampler;
	};
}